<?php

namespace fastphp\util;

use fastphp\Db;
use fastphp\Config;

/**
 * CRUD代码生成器
 * 用于自动生成模型、控制器和视图文件
 */
class CrudGenerator
{
    /**
     * @var Db 数据库实例
     */
    protected Db $db;

    /**
     * @var Config 配置实例
     */
    protected Config $config;

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->config = new Config();
        $this->db = new Db();
    }

    /**
     * 获取数据库中所有表名
     * @return array 表名列表
     */
    public function getTables(): array
    {
        $prefix = $this->config->get('database.prefix', '');
        $sql = "SHOW TABLES";
        $tables = $this->db->link->query($sql)->fetchAll(\PDO::FETCH_NUM);
        $result = [];

        foreach ($tables as $table) {
            $tableName = $table[0];
            // 移除表前缀
            if ($prefix && strpos($tableName, $prefix) === 0) {
                $result[] = [
                    'name' => $tableName,
                    'display_name' => substr($tableName, strlen($prefix))
                ];
            } else {
                $result[] = [
                    'name' => $tableName,
                    'display_name' => $tableName
                ];
            }
        }

        return $result;
    }

    /**
     * 获取表的列信息
     * @param string $tableName 表名
     * @return array 列信息数组
     */
    public function getColumns(string $tableName): array
    {
        $sql = "DESCRIBE {$tableName}";
        $columns = $this->db->link->query($sql)->fetchAll(\PDO::FETCH_ASSOC);

        return $columns;
    }

    /**
     * 获取表的主键
     * @param string $tableName 表名
     * @return string 主键列名
     */
    public function getPrimaryKey(string $tableName): string
    {
        $columns = $this->getColumns($tableName);
        foreach ($columns as $column) {
            if ($column['Key'] === 'PRI') {
                return $column['Field'];
            }
        }
        return 'id';
    }

    /**
     * 生成模型文件
     * @param string $tableName 表名
     * @param string $tableDisplayName 表的显示名称（不含前缀）
     * @return bool 是否生成成功
     */
    public function generateModel(string $tableName, string $tableDisplayName): bool
    {
        // 生成模型名称（驼峰命名，首字母大写）
        $modelName = $this->toCamelCase($tableDisplayName, true);

        // 生成模型文件内容
        $columns = $this->getColumns($tableName);
        $primaryKey = $this->getPrimaryKey($tableName);

        // 生成验证规则
        $rules = [];
        foreach ($columns as $column) {
            if ($column['Field'] === $primaryKey) continue;

            $rule = "'{$column['Field']}' => '";
            // 必填字段
            if (strpos($column['Null'], 'NO') !== false && $column['Default'] === null) {
                $rule .= 'required|';
            }

            // 数据类型规则
            $type = $column['Type'];
            if (strpos($type, 'int') !== false) {
                $rule .= 'integer';
            } elseif (strpos($type, 'varchar') !== false || strpos($type, 'text') !== false) {
                $rule .= 'string';
            } elseif (strpos($type, 'decimal') !== false || strpos($type, 'float') !== false) {
                $rule .= 'numeric';
            }

            $rules[] = $rule . "'";
        }

        $rulesStr = implode(",\n        ", $rules);

        $content = <<<EOF
<?php

namespace application\model;

use fastphp\Model;

/**
 * {$modelName}模型
 */
class {$modelName} extends Model
{
    /**
     * 表名
     * @var string
     */
    protected string \$_table = '{$tableName}';

    /**
     * 验证规则
     * @var array
     */
    protected array \$_rules = [
        {$rulesStr}
    ];
}
EOF;

        // 确保目录存在
        $modelDir = APP_PATH . '/application/model';
        Directory::create($modelDir, 0777, true);

        // 写入文件
        return file_put_contents("{$modelDir}/{$modelName}.php", $content) !== false;
    }

    /**
     * 生成控制器文件
     * @param string $tableName 表名
     * @param string $tableDisplayName 表的显示名称（不含前缀）
     * @return bool 是否生成成功
     */
    public function generateController(string $tableName, string $tableDisplayName): bool
    {
        // 生成控制器名称（驼峰命名，首字母大写）
        $controllerName = $this->toCamelCase($tableDisplayName, true);
        $modelName = $controllerName;
        $lowerName = $this->toCamelCase($tableDisplayName, false);

        // 获取列信息
        $columns = $this->getColumns($tableName);
        $primaryKey = $this->getPrimaryKey($tableName);

        // 生成字段列表（除主键外）
        $fields = [];
        foreach ($columns as $column) {
            if ($column['Field'] !== $primaryKey) {
                $fields[] = $column['Field'];
            }
        }

        $fieldsStr = implode("', '", $fields);

        $content = <<<EOF
<?php

namespace application\controller;

use application\model\\$modelName as ${modelName}Model;
use application\BaseController;

/**
 * {$controllerName}控制器
 */
class {$controllerName} extends BaseController
{
    /**
     * 模型实例
     * @var {$modelName}Model
     */
    protected \$model;

    /**
     * 构造函数
     */
    public function __construct(string \$controller, string \$action)
    {
        parent::__construct(\$controller, \$action);
        \$this->model = new {$modelName}Model();
    }

    /**
     * 列表页面
     */
    public function index()
    {
        \$page = \$this->request()->get('page', 1);
        \$pageSize = \$this->request()->get('page_size', 10);
        
        \$data = \$this->model->page(\$page, \$pageSize)->select();
        
        \$this->set('data', \$data);
        \$this->set('active', '{$lowerName}');
        \$this->render("admin/{$lowerName}/index");
    }

    /**
     * 添加页面
     */
    public function add()
    {
        \$this->set('active', '{$lowerName}');
        \$this->render("admin/{$lowerName}/add");
    }

    /**
     * 保存数据
     */
    public function save()
    {
        \$data = \$this->request()->post();
        
        if (\$this->model->create(\$data)) {
            \$this->success(['url' => '/admin/{$lowerName}'],'保存成功');
        } else {
            \$this->error(\$this->model->_errors[0] ?? '保存失败');
        }
    }

    /**
     * 编辑页面
     */
    public function edit()
    {
        \$id = \$this->request()->get('id');
        \$data = \$this->model->find(['{$primaryKey}' => \$id]);
        
        \$this->set('data', \$data);
        \$this->set('active', '{$lowerName}');
        \$this->render("admin/{$lowerName}/edit");
    }

    /**
     * 更新数据
     */
    public function update()
    {
        \$id = \$this->request()->post('{$primaryKey}');
        \$data = \$this->request()->post();
        
        if (\$this->model->update(\$data, ['{$primaryKey}' => \$id])) {
            \$this->success(['url' => '/admin/{$lowerName}'],'更新成功');
        } else {
            \$this->error(\$this->model->_errors[0] ?? '更新失败');
        }
    }

    /**
     * 删除数据
     */
    public function delete()
    {
        \$id = \$this->request()->get('id');
        
        if (\$this->model->delete(['{$primaryKey}' => \$id])) {
            \$this->success([],'删除成功');
        } else {
            \$this->error('删除失败');
        }
    }
}
EOF;

        // 确保目录存在
        $controllerDir = APP_PATH . '/application/controller';
        Directory::create($controllerDir, 0777, true);

        // 写入文件
        return file_put_contents("{$controllerDir}/{$controllerName}.php", $content) !== false;
    }

    /**
     * 生成视图文件
     * @param string $tableName 表名
     * @param string $tableDisplayName 表的显示名称（不含前缀）
     * @return bool 是否生成成功
     */
    public function generateViews(string $tableName, string $tableDisplayName): bool
    {
        $lowerName = $this->toCamelCase($tableDisplayName, false);
        $columns = $this->getColumns($tableName);
        $primaryKey = $this->getPrimaryKey($tableName);

        // 生成视图目录
        $viewDir = APP_PATH . "/application/view/admin/{$lowerName}";
        Directory::create($viewDir, 0777, true);

        // 生成index.tpl
        $indexContent = $this->generateIndexView($columns, $primaryKey, $lowerName);
        if (!file_put_contents("{$viewDir}/index.tpl", $indexContent)) {
            return false;
        }

        // 生成add.tpl
        $addContent = $this->generateAddView($columns, $primaryKey, $lowerName);
        if (!file_put_contents("{$viewDir}/add.tpl", $addContent)) {
            return false;
        }

        // 生成edit.tpl
        $editContent = $this->generateEditView($columns, $primaryKey, $lowerName);
        if (!file_put_contents("{$viewDir}/edit.tpl", $editContent)) {
            return false;
        }

        return true;
    }

    /**
     * 生成索引视图
     */
    protected function generateIndexView(array $columns, string $primaryKey, string $lowerName): string
    {
        $displayName = $this->formatDisplayName($lowerName);

        // 生成表头
        $headers = [];
        $cells = [];
        foreach ($columns as $column) {
            $fieldName = $column['Field'];
            $headerName = $this->formatDisplayName($fieldName);

            $headers[] = "<th>{$headerName}</th>";
            if ($fieldName === $primaryKey) {
                $cells[] = "<td>{{\$item.{$fieldName}}}</td>";
            } else {
                // 根据字段类型生成不同的单元格内容
                $cells[] = "<td>{{\$item.{$fieldName}}}</td>";
            }
        }
        $headers[] = "<th>操作</th>";

        $headersStr = implode("\n                ", $headers);
        $cellsStr = implode("\n                    ", $cells);

        return <<<EOF
{{extends file='admin/base.tpl'}}

{{block name='title'}}{$displayName}管理{{/block}}

{{block name='content'}}
<div class="container-fluid">
    <div class="card shadow mb-4">
        <div class="card-header py-3">
            <h6 class="m-0 font-weight-bold text-primary">{$displayName}列表</h6>
        </div>
        <div class="card-body">
            <div class="mb-4">
                <a href="/admin/{$lowerName}/add" class="btn btn-primary">
                    <i class="fas fa-plus"></i> 添加{$displayName}
                </a>
            </div>
            <div class="table-responsive">
                <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
                    <thead>
                        <tr>
                            {$headersStr}
                        </tr>
                    </thead>
                    <tbody>
                        {{foreach \$data as \$item}}
                        <tr>
                            {$cellsStr}
                            <td>
                                <a href="/admin/{$lowerName}/edit?id={{\$item.{$primaryKey}}}" class="btn btn-warning btn-sm">编辑</a>
                                <a href="/admin/{$lowerName}/delete?id={{\$item.{$primaryKey}}}" class="btn btn-danger btn-sm delete-btn">删除</a>
                            </td>
                        </tr>
                        {{/foreach}}
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

<script>
// 删除确认
$(document).on('click', '.delete-btn', function(e) {
    e.preventDefault();
    if (confirm('确定要删除这条记录吗？')) {
        window.location.href = $(this).attr('href');
    }
});
</script>
{{/block}}
EOF;
    }

    /**
     * 生成添加视图
     */
    protected function generateAddView(array $columns, string $primaryKey, string $lowerName): string
    {
        $displayName = $this->formatDisplayName($lowerName);
        $formFields = [];

        foreach ($columns as $column) {
            if ($column['Field'] === $primaryKey) continue;

            $fieldName = $column['Field'];
            $displayFieldName = $this->formatDisplayName($fieldName);
            $required = strpos($column['Null'], 'NO') !== false && $column['Default'] === null ? 'required' : '';

            // 根据字段类型生成不同的表单控件
            $type = $column['Type'];
            if (strpos($type, 'int') !== false) {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <input type="number" class="form-control" id="{$fieldName}" name="{$fieldName}" {$required}>
        </div>
EOF;
            } elseif (strpos($type, 'text') !== false) {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <textarea class="form-control" id="{$fieldName}" name="{$fieldName}" rows="4" {$required}></textarea>
        </div>
EOF;
            } else {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <input type="text" class="form-control" id="{$fieldName}" name="{$fieldName}" {$required}>
        </div>
EOF;
            }

            $formFields[] = $formField;
        }

        $formFieldsStr = implode("\n", $formFields);

        return <<<EOF
{{extends file='admin/base.tpl'}}

{{block name='title'}}添加{$displayName}{{/block}}

{{block name='content'}}
<div class="container-fluid">
    <div class="card shadow mb-4">
        <div class="card-header py-3">
            <h6 class="m-0 font-weight-bold text-primary">添加{$displayName}</h6>
        </div>
        <div class="card-body">
            <form id="add-form" action="/admin/{$lowerName}/save" method="post">
                {$formFieldsStr}
                <button type="submit" class="btn btn-primary">保存</button>
                <a href="/admin/{$lowerName}" class="btn btn-secondary">取消</a>
            </form>
        </div>
    </div>
</div>

<script>
// 表单验证
$(document).ready(function() {
    $('#add-form').validate({
        errorElement: 'div',
        errorClass: 'invalid-feedback',
        highlight: function(element, errorClass, validClass) {
            $(element).addClass('is-invalid');
        },
        unhighlight: function(element, errorClass, validClass) {
            $(element).removeClass('is-invalid');
        }
    });
});
</script>
{{/block}}
EOF;
    }

    /**
     * 生成编辑视图
     */
    protected function generateEditView(array $columns, string $primaryKey, string $lowerName): string
    {
        $displayName = $this->formatDisplayName($lowerName);
        $formFields = [];

        foreach ($columns as $column) {
            $fieldName = $column['Field'];
            $displayFieldName = $this->formatDisplayName($fieldName);
            $required = strpos($column['Null'], 'NO') !== false && $column['Default'] === null ? 'required' : '';

            // 根据字段类型生成不同的表单控件
            $type = $column['Type'];
            if ($fieldName === $primaryKey) {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <input type="text" class="form-control" id="{$fieldName}" name="{$fieldName}" value="{{\$data.{$fieldName}}" readonly>
        </div>
EOF;
            } elseif (strpos($type, 'int') !== false) {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <input type="number" class="form-control" id="{$fieldName}" name="{$fieldName}" value="{{\$data.{$fieldName}}" {$required}>
        </div>
EOF;
            } elseif (strpos($type, 'text') !== false) {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <textarea class="form-control" id="{$fieldName}" name="{$fieldName}" rows="4" {$required}>{{\$data.{$fieldName}}</textarea>
        </div>
EOF;
            } else {
                $formField = <<<EOF
        <div class="form-group">
            <label for="{$fieldName}">{$displayFieldName}</label>
            <input type="text" class="form-control" id="{$fieldName}" name="{$fieldName}" value="{{\$data.{$fieldName}}" {$required}>
        </div>
EOF;
            }

            $formFields[] = $formField;
        }

        $formFieldsStr = implode("\n", $formFields);

        return <<<EOF
{{extends file='admin/base.tpl'}}

{{block name='title'}}编辑{$displayName}{{/block}}

{{block name='content'}}
<div class="container-fluid">
    <div class="card shadow mb-4">
        <div class="card-header py-3">
            <h6 class="m-0 font-weight-bold text-primary">编辑{$displayName}</h6>
        </div>
        <div class="card-body">
            <form id="edit-form" action="/admin/{$lowerName}/update" method="post">
                {$formFieldsStr}
                <button type="submit" class="btn btn-primary">保存</button>
                <a href="/admin/{$lowerName}" class="btn btn-secondary">取消</a>
            </form>
        </div>
    </div>
</div>

<script>
// 表单验证
$(document).ready(function() {
    $('#edit-form').validate({
        errorElement: 'div',
        errorClass: 'invalid-feedback',
        highlight: function(element, errorClass, validClass) {
            $(element).addClass('is-invalid');
        },
        unhighlight: function(element, errorClass, validClass) {
            $(element).removeClass('is-invalid');
        }
    });
});
</script>
{{/block}}
EOF;
    }

    /**
     * 将字符串转换为驼峰命名
     * @param string $str 原始字符串
     * @param bool $ucfirst 是否首字母大写
     * @return string 驼峰命名的字符串
     */
    public function toCamelCase(string $str, bool $ucfirst = true): string
    {
        // 修改后
        $str = preg_replace_callback('/[_-]+(\w)/', function ($matches) {
            return strtoupper($matches[1]);
        }, $str);

        // 根据需要处理首字母
        if ($ucfirst) {
            $str = ucfirst($str);
        } else {
            $str = lcfirst($str);
        }

        return $str;
    }

    /**
     * 格式化显示名称
     * @param string $str 原始字符串
     * @return string 格式化后的显示名称
     */
    protected function formatDisplayName(string $str): string
    {
        // 替换下划线和连字符为空格
        $str = str_replace(['_', '-'], ' ', $str);

        // 单词首字母大写
        return ucwords($str);
    }

    /**
     * 完整生成CRUD代码
     * @param string $tableName 表名
     * @param string $tableDisplayName 表的显示名称（不含前缀）
     * @return array 生成结果
     */
    public function generate(string $tableName, string $tableDisplayName): array
    {
        $results = [
            'model' => false,
            'controller' => false,
            'views' => false
        ];

        try {
            // 生成模型
            $results['model'] = $this->generateModel($tableName, $tableDisplayName);

            // 生成控制器
            $results['controller'] = $this->generateController($tableName, $tableDisplayName);

            // 生成视图
            $results['views'] = $this->generateViews($tableName, $tableDisplayName);
        } catch (\Exception $e) {
            return ['error' => $e->getMessage()];
        }

        return $results;
    }
}
